package com.study.hisapi.utils;

import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@Component
public class SpringReflectUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    private SpringReflectUtils() {
    }

    /**
     * 通过Spring上下文获取Bean实例
     *
     * @param beanName Bean名称
     * @param <T>      Bean类型
     * @return Bean实例
     */
    public static <T> @NotNull T getBean(String beanName) {
        return (T) applicationContext.getBean(beanName);
    }

    /**
     * 通过类型从Spring上下文中获取Bean实例
     *
     * @param beanType Bean类型
     * @param <T>      Bean类型
     * @return Bean实例
     */
    public static <T> @NotNull T getBean(Class<T> beanType) {
        return applicationContext.getBean(beanType);
    }

    /**
     * 使用反射设置对象属性值
     *
     * @param targetObject 目标对象
     * @param fieldName    属性名
     * @param fieldValue   属性值
     * @throws NoSuchFieldException   如果找不到指定的字段
     * @throws IllegalAccessException 如果无法访问字段
     */
    public static void setFieldValue(@NotNull Object targetObject, String fieldName, Object fieldValue) throws NoSuchFieldException, IllegalAccessException {
        Field field = targetObject.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(targetObject, fieldValue);
    }

    /**
     * 使用反射调用对象方法
     *
     * @param targetObject 目标对象
     * @param methodName   方法名
     * @param args         方法参数
     * @return 方法返回值
     * @throws NoSuchMethodException     如果找不到指定的方法
     * @throws InvocationTargetException 如果方法调用过程中出现异常
     * @throws IllegalAccessException    如果无法访问方法
     */
    public static Object invokeMethod(@NotNull Object targetObject, String methodName, Object... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method method = targetObject.getClass().getMethod(methodName, convertArgTypes(args));
        return method.invoke(targetObject, args);
    }

    /**
     * 将方法参数对象数组转换为对应的Class数组
     *
     * @param args 方法参数对象数组
     * @return 对应的Class数组
     */
    @Contract(value = "null -> new", pure = true)
    private static Class<?> @NotNull [] convertArgTypes(Object[] args) {
        if (args == null || args.length == 0) {
            return new Class<?>[0];
        }
        Class<?>[] argTypes = new Class<?>[args.length];
        for (int i = 0; i < args.length; i++) {
            argTypes[i] = args[i].getClass();
        }
        return argTypes;
    }

    /**
     * 获取Spring容器中所有Bean的定义信息
     *
     * @return Bean定义信息映射（Bean名称 -> Bean定义）
     */
    public static String @NotNull [] getAllBeanDefinitions() {
        ConfigurableListableBeanFactory factory = (ConfigurableListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
        return factory.getBeanDefinitionNames();
    }

    @Override
    public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
        SpringReflectUtils.applicationContext = applicationContext;
    }
}

